package cn.skynethome.redisx.spring;

import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import cn.skynethome.redisx.FileUtil;
import cn.skynethome.redisx.RedisMasterSlaveUtil;
import cn.skynethome.redisx.SerializationAndCompressUtils;
import cn.skynethome.redisx.common.ms.MasterSlaveJedis;
import cn.skynethome.redisx.common.ms.MasterSlaveJedisPool;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.util.Pool;

/**
  * 项目名称:[redisx]
  * 包:[cn.skynethome.redisx.spring]    
  * 文件名称:[RedisXMasterSlave]  
  * 描述:[和spring集成，一主多备。]
  * 创建人:[陆文斌]
  * 创建时间:[2017年1月4日 下午2:04:34]   
  * 修改人:[陆文斌]   
  * 修改时间:[2017年1月4日 下午2:04:34]   
  * 修改备注:[说明本次修改内容]  
  * 版权所有:luwenbin006@163.com
  * 版本:[v1.0]
 */
public class RedisXMasterSlave {
	protected Logger logger = Logger.getLogger(RedisMasterSlaveUtil.class);

	// redis_master 服务器
	private HostAndPort master_server;

	// redis_salve 服务器
	private Set<HostAndPort> slave_server = null;
	
	private String master;
	
	private String slave;
	

	// 访问密码
	private String AUTH = null;

	// 可用连接实例的最大数目，默认值为8；
	// 如果赋值为-1，则表示不限制；如果pool已经分配了maxActive个jedis实例，则此时pool的状态为exhausted(耗尽)。
	private int MAX_ACTIVE = 0;

	// 控制一个pool最多有多少个状态为idle(空闲的)的jedis实例，默认值也是8。
	private int MAX_IDLE = 0;

	// 等待可用连接的最大时间，单位毫秒，默认值为-1，表示永不超时。如果超过等待时间，则直接抛出JedisConnectionException；
	private int MAX_WAIT = 0;

	// 超时时间
	private int TIMEOUT = 0;

	// 在borrow一个jedis实例时，是否提前进行validate操作；如果为true，则得到的jedis实例均是可用的；
	private boolean TEST_ON_BORROW = false;

	private Pool<MasterSlaveJedis> masterSlaveJedisPool = null;


    private String configPath="properties/redis_master_salve.properties"; //默认配置
    
    private boolean configFlag = false;
    
    
    
    
	public RedisXMasterSlave() {
		super();
		// TODO Auto-generated constructor stub
	}

	

	public String getMaster() {
		return master;
	}



	public void setMaster(String master) {
		this.master = master;
		String[] master_arr = master.split(":");
		this.master_server = new HostAndPort(master_arr[0],Integer.valueOf(master_arr[1]));
	}



	public String getSlave() {
		return slave;
	}



	public void setSlave(String slave) {
		this.slave = slave;
		String[] slave_arr = this.slave.split(",");
		slave_server = new LinkedHashSet<HostAndPort>();
		
		for(String adds : slave_arr)
		{
			String[] addarr = adds.split(":");
			this.slave_server.add(new HostAndPort(addarr[0],Integer.valueOf(addarr[1])));
		}
		
	}



	public String getAUTH() {
		return AUTH;
	}

	public void setAUTH(String aUTH) {
		AUTH = aUTH;
	}

	public int getMAX_ACTIVE() {
		return MAX_ACTIVE;
	}

	public void setMAX_ACTIVE(int mAX_ACTIVE) {
		MAX_ACTIVE = mAX_ACTIVE;
	}

	public int getMAX_IDLE() {
		return MAX_IDLE;
	}

	public void setMAX_IDLE(int mAX_IDLE) {
		MAX_IDLE = mAX_IDLE;
	}

	public int getMAX_WAIT() {
		return MAX_WAIT;
	}

	public void setMAX_WAIT(int mAX_WAIT) {
		MAX_WAIT = mAX_WAIT;
	}

	public int getTIMEOUT() {
		return TIMEOUT;
	}

	public void setTIMEOUT(int tIMEOUT) {
		TIMEOUT = tIMEOUT;
	}

	public boolean isTEST_ON_BORROW() {
		return TEST_ON_BORROW;
	}

	public void setTEST_ON_BORROW(boolean tEST_ON_BORROW) {
		TEST_ON_BORROW = tEST_ON_BORROW;
	}

	public String getConfigPath() {
		return configPath;
	}

	public void setConfigPath(String configPath) {
		this.configPath = configPath;
	}

	public boolean isConfigFlag() {
		return configFlag;
	}

	public void setConfigFlag(boolean configFlag) {
		this.configFlag = configFlag;
	}

	/**
	 * 初始化Redis连接池
	 */
	private void initialPool() {

		initRedisConfigValue();
		JedisPoolConfig config = new JedisPoolConfig();
		config.setMaxTotal(MAX_ACTIVE);
		config.setMaxIdle(MAX_IDLE);
		config.setMaxWaitMillis(MAX_WAIT);
		config.setTestOnBorrow(TEST_ON_BORROW);
		
		if(StringUtils.isEmpty(AUTH))
		{
			masterSlaveJedisPool = new MasterSlaveJedisPool(master_server, slave_server, config, TIMEOUT);
		}else
		{
			masterSlaveJedisPool = new MasterSlaveJedisPool(master_server, slave_server, config, TIMEOUT, AUTH);
		}
	}

	private void initRedisConfigValue() {
		// TODO Auto-generated method stub
		if(configFlag)
		{
			
		this.setMaster(FileUtil.getPropertyValue(configPath, "master_server"));		
		this.setSlave(FileUtil.getPropertyValue(configPath, "slave_server"));
		AUTH = FileUtil.getPropertyValue(configPath, "auth");
		MAX_ACTIVE = FileUtil.getPropertyValueInt(configPath, "max_active");

		MAX_IDLE = FileUtil.getPropertyValueInt(configPath, "max_idle");

		MAX_WAIT = FileUtil.getPropertyValueInt(configPath, "max_wait");

		TIMEOUT = FileUtil.getPropertyValueInt(configPath, "timeout");

		TEST_ON_BORROW = FileUtil.getPropertyValueBoolean(configPath, "test_on_borrow");

		FileUtil.remove(configPath);
		}
	}

	/**
	 * 在多线程环境同步初始化
	 */
	private synchronized void poolInit() {
		if (masterSlaveJedisPool == null) {
			initialPool();
		}
	}

	/**
	 * 同步获取Jedis实例
	 * 
	 * @return Jedis
	 */
	public synchronized MasterSlaveJedis getJedis() {
		if (masterSlaveJedisPool == null) {
			poolInit();
		}
		MasterSlaveJedis jedis = null;
		try {
			if (masterSlaveJedisPool != null) {
				jedis = masterSlaveJedisPool.getResource();
			}
		} catch (Exception e) {
			returnResource(jedis);
			logger.error("Get jedis error : " + e);
		}

		return jedis;
	}

	/**
	 * 释放jedis资源
	 * 
	 * @param jedis
	 */
	public void returnResource(final MasterSlaveJedis jedis) {
		if (jedis != null) {
			jedis.close();
		}
	}

	/**
	 * 设置 String
	 * 
	 * @param key
	 * @param value
	 */
	public String setString(String key, String value) {
		MasterSlaveJedis masterSlaveJedis = null;
		String r_str = "nil";
		try {
			value = StringUtils.isEmpty(value) ? "" : value;
			masterSlaveJedis = getJedis();
			r_str = masterSlaveJedis.set(key, value);
		} catch (Exception e) {
			// return jedis object to pll and set jedis is null.
			returnResource(masterSlaveJedis);
			masterSlaveJedis = null;
			logger.error("Set key error : " + e);
		} finally {
			returnResource(masterSlaveJedis);
		}
		return r_str;
	}

	/**
	 * 设置 过期时间
	 * 
	 * @param key
	 * @param seconds
	 *            以秒为单位
	 * @param value
	 */
	public String setString(String key, String value, int expirationTime) {
		MasterSlaveJedis masterSlaveJedis = null;
		String r_str = "nil";
		try {
			value = StringUtils.isEmpty(value) ? "" : value;
			masterSlaveJedis = getJedis();
			r_str = masterSlaveJedis.setex(key, expirationTime, value);
		} catch (Exception e) {
			// return jedis object to pll and set jedis is null.
			returnResource(masterSlaveJedis);
			masterSlaveJedis = null;
			logger.error("Set keyex error : " + e);
		} finally {
			returnResource(masterSlaveJedis);
		}

		return r_str;
	}

	public Set<byte[]> getKeys(byte[] keys) {
		MasterSlaveJedis masterSlaveJedis = getJedis();
		Set<byte[]> set_ = masterSlaveJedis.opsForSlave().keys(keys);
		returnResource(masterSlaveJedis);
		return set_;

	}

	public Set<String> getKeys(String keys) {
		MasterSlaveJedis masterSlaveJedis = getJedis();
		Set<String> set_ = masterSlaveJedis.opsForSlave().keys(keys);
		returnResource(masterSlaveJedis);
		return set_;

	}

	public Set<byte[]> hkeys(byte[] hkeys) {
		MasterSlaveJedis masterSlaveJedis = getJedis();
		Set<byte[]> set_ = masterSlaveJedis.opsForSlave().hkeys(hkeys);
		returnResource(masterSlaveJedis);
		return set_;
	}

	public Set<String> hkeys(String hkeys) {
		MasterSlaveJedis masterSlaveJedis = getJedis();
		Set<String> set_ = masterSlaveJedis.opsForSlave().hkeys(hkeys);
		returnResource(masterSlaveJedis);
		return set_;
	}

	public Collection<byte[]> hvals(byte[] hvals) {
		MasterSlaveJedis masterSlaveJedis = getJedis();
		Collection<byte[]> collection = masterSlaveJedis.opsForSlave().hvals(hvals);
		returnResource(masterSlaveJedis);
		return collection;
	}

	public List<String> hvals(String hvals) {
		MasterSlaveJedis masterSlaveJedis = getJedis();
		List<String> list = masterSlaveJedis.opsForSlave().hvals(hvals);
		returnResource(masterSlaveJedis);
		return list;
	}

	/**
	 * 获取String值
	 * 
	 * @param key
	 * @return value
	 */
	public String getString(String key) {
		MasterSlaveJedis masterSlaveJedis = getJedis();
		String r_str = null;
		if (masterSlaveJedis == null || !masterSlaveJedis.exists(key)) {
			r_str = null;
			returnResource(masterSlaveJedis);
		} else {
			r_str = masterSlaveJedis.opsForSlave().get(key);
			returnResource(masterSlaveJedis);
		}

		return r_str;
	}

	public String setObject(String key, Object obj, int expirationTime) {
		String r_t = null;
		MasterSlaveJedis masterSlaveJedis = getJedis();
		try {

			r_t = masterSlaveJedis.setex(key.getBytes(), expirationTime,
					SerializationAndCompressUtils.fastSerialize(obj));

		} catch (Exception e) {
			// return jedis object to pll and set jedis is null.
			returnResource(masterSlaveJedis);
			masterSlaveJedis = null;
			e.printStackTrace();
		} finally {
			returnResource(masterSlaveJedis);
		}
		return r_t;

	}

	public String setObject(String key, Object obj) {
		String r_t = null;
		MasterSlaveJedis masterSlaveJedis = getJedis();
		try {
			r_t = masterSlaveJedis.set(key.getBytes(), SerializationAndCompressUtils.fastSerialize(obj));

		} catch (Exception e) {
			// return jedis object to pll and set jedis is null.
			returnResource(masterSlaveJedis);
			masterSlaveJedis = null;
			e.printStackTrace();
		} finally {
			returnResource(masterSlaveJedis);

		}
		return r_t;

	}

	public Long del(String key) {
		long d_l = 0l;
		MasterSlaveJedis masterSlaveJedis = getJedis();
		try {

			d_l = masterSlaveJedis.del(key);

		} catch (Exception e) {
			// return jedis object to pll and set jedis is null.
			returnResource(masterSlaveJedis);
			masterSlaveJedis = null;
			e.printStackTrace();
		} finally {

			returnResource(masterSlaveJedis);

		}
		return d_l;

	}

	public Long del(byte[] key) {
		Long d_l = 0l;
		MasterSlaveJedis masterSlaveJedis = getJedis();
		try {

			d_l = masterSlaveJedis.del(key);

		} catch (Exception e) {

			// return jedis object to pll and set jedis is null.
			returnResource(masterSlaveJedis);
			masterSlaveJedis = null;
			e.printStackTrace();
		} finally {

			returnResource(masterSlaveJedis);

		}
		return d_l;

	}

	public Long del(byte[]... key) {
		Long d_l = 0l;
		MasterSlaveJedis masterSlaveJedis = getJedis();
		try {
			d_l = masterSlaveJedis.del(key);
		} catch (Exception e) {
			// return jedis object to pll and set jedis is null.
			returnResource(masterSlaveJedis);
			masterSlaveJedis = null;
			e.printStackTrace();
		} finally {

			returnResource(masterSlaveJedis);

		}
		return d_l;

	}

	/**
	 * 获取Object
	 * 
	 * @param key
	 * @return
	 * @return value
	 */
	public <T> T getObject(String key, Class<T> classz) {
		MasterSlaveJedis masterSlaveJedis = getJedis();
		T t = null;
		if (masterSlaveJedis == null || !masterSlaveJedis.exists(key)) {
			t = null;
			returnResource(masterSlaveJedis);
		} else {
			byte[] value = masterSlaveJedis.opsForSlave().get(key.getBytes());

			try {

				t = classz.cast(SerializationAndCompressUtils.fastDeserialize(value));

			} catch (Exception e) {
				// return jedis object to pll and set jedis is null.
				returnResource(masterSlaveJedis);
				masterSlaveJedis = null;

				e.printStackTrace();
			} finally {
				returnResource(masterSlaveJedis);
			}
		}
		return t;
	}

	public Long hdel(String key, String field) {
		Long r_l = 0l;
		MasterSlaveJedis masterSlaveJedis = getJedis();
		if (masterSlaveJedis == null) {
			r_l = 0l;
		} else {
			try {

				r_l = masterSlaveJedis.hdel(key, field);
			} catch (Exception e) {
				// return jedis object to pll and set jedis is null.
				returnResource(masterSlaveJedis);
				masterSlaveJedis = null;

				e.printStackTrace();
			} finally {

				returnResource(masterSlaveJedis);

			}
		}

		return r_l;
	}

	public Map<String, String> hgetall(String key) {
		MasterSlaveJedis masterSlaveJedis = getJedis();
		Map<String, String> map = null;
		if (masterSlaveJedis == null) {
			map = null;

		} else {
			try {
				map = masterSlaveJedis.opsForSlave().hgetAll(key);

			} catch (Exception e) {

				// return jedis object to pll and set jedis is null.
				returnResource(masterSlaveJedis);
				masterSlaveJedis = null;
				e.printStackTrace();
			} finally {

				returnResource(masterSlaveJedis);

			}
		}

		return map;
	}

	public Long hincrBy(String key, String field, long value) {
		MasterSlaveJedis masterSlaveJedis = getJedis();
		Long r_l = 0l;
		if (masterSlaveJedis == null) {
			r_l = 0l;
		} else {
			try {
				r_l = masterSlaveJedis.hincrBy(key, field, value);
			} catch (Exception e) {
				// return jedis object to pll and set jedis is null.
				returnResource(masterSlaveJedis);
				masterSlaveJedis = null;
				e.printStackTrace();
			} finally {
				if (null != masterSlaveJedis) {
					returnResource(masterSlaveJedis);
				}
			}
		}

		return r_l;
	}
	
	public boolean exists(byte[] key)
    {
        MasterSlaveJedis masterSlaveJedis = getJedis();
        boolean isExists = false;
        
        if (masterSlaveJedis == null)
        {
            isExists = false;
        }
        else
        {
            isExists = masterSlaveJedis.exists(key);
            returnResource(masterSlaveJedis);
        }
        
        return isExists;
    }
    
    public boolean exists(String key)
    {
        if( null==key )
        {
            return false;
        }
        
        return exists(key.getBytes());
    }
    
    public Long zadd(String key, double score, String member)
    {
        MasterSlaveJedis masterSlaveJedis = getJedis();
        Long r_l = 0l;
        if (masterSlaveJedis == null)
        {
            r_l = 0l;
        }
        else
        {
            r_l = masterSlaveJedis.zadd(key, score, member);
            returnResource(masterSlaveJedis);
        }

        return r_l;
    }

    public Set<String> zrange(String key, long start, long end)
    {

        Set<String> set = null;
        MasterSlaveJedis masterSlaveJedis = getJedis();
        if (masterSlaveJedis == null)
        {
            set = null;

        }
        else
        {
            set = masterSlaveJedis.zrange(key, start, end);
            returnResource(masterSlaveJedis);
        }

        return set;
    }

    public Double zscore(String key, String member)
    {

        Double d_ = 0d;
        MasterSlaveJedis masterSlaveJedis = getJedis();
        if (masterSlaveJedis == null)
        {
            d_ = 0d;

        }
        else
        {
            d_ = masterSlaveJedis.zscore(key, member);
            returnResource(masterSlaveJedis);
        }

        return d_;
    }
}